﻿using System.Security.Cryptography;
using System.Text;

namespace Sunxsh.Es.Core.Session;

public class AESGcmCrypto
{
    /// <summary>
    /// 加密
    /// </summary>
    public static byte[] Encrypt(byte[] key, Span<byte> plain)
    {
        var buffer = new byte[plain.Length + NonceSize + TagSize];

        split(buffer, out var tag, out var nonce, out var data);

        //用随机数生成nonce
        _rnd.NextBytes(nonce);

        using var aes = new AesGcm(key, tag.Length);
        aes.Encrypt(nonce, plain, data, tag);

        return buffer;
    }


    public static string EncryptString(string plain)
    {
        var plainBytes = Encoding.UTF8.GetBytes(plain);
        var content = Encrypt(DefaultKey, plainBytes);
        return Convert.ToBase64String(content);
    }



    static Random _rnd = new();


    /// <summary>
    /// 解密
    /// </summary>
    public static byte[] Decrypt(byte[] key, byte[] content)
    {
        split(content, out var tag, out var nonce, out var cipher);
        var plain = new byte[cipher.Length];

        using var aes = new AesGcm(key, tag.Length);
        aes.Decrypt(nonce, cipher, tag, plain);

        return plain;
    }

    public static string DecryptString(string base64)
    {
        var content = Convert.FromBase64String(base64);
        var plainBytes = Decrypt(DefaultKey, content);
        return Encoding.UTF8.GetString(plainBytes);
    }

    /// <summary>
    /// 生成key
    /// </summary>
    public static byte[] MakeKey(string keyText)
    {
        //实际的key要求定长，通过 md5(key+salt)生成
        var content = Encoding.Default.GetBytes(keyText + "_Sunxsh.Es");
        return MD5.HashData(content);
    }

    public static byte[] DefaultKey { get; } = MakeKey("Sunxsh.Es");

    static void split(Span<byte> buffer, out Span<byte> tag, out Span<byte> nonce, out Span<byte> data)
    {
        //将buffer划分为 tag， noce， data 三个部分一次存放

        tag = buffer.Slice(0, TagSize);
        nonce = buffer.Slice(TagSize, NonceSize);
        data = buffer.Slice(TagSize + NonceSize);
    }

    static int NonceSize => AesGcm.NonceByteSizes.MinSize;
    static int TagSize => AesGcm.TagByteSizes.MinSize;
}